/*
 *only support for "exact division"
 *
 *
 * */



#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"
#include "dma.h"

#define alpha_d 0.0876
#define beta_d  0.0765

__thread_local volatile unsigned long get_reply,put_reply;


//core region
__thread_local_fix double slave[2][BLKX][BLKY];
//__thread_local double A_lhs[BLKX][BLKY];
//__thread_local double A_rhs[BLKX][BLKY];
//up and down shade region
__thread_local double A_us[BLKY];
__thread_local double A_ds[BLKY];
//left and right shade  region
__thread_local double A_ls[BLKX];
__thread_local double A_rs[BLKX];
//Record the logical information of the last time executed task.
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
__thread_local dma_desc dma_d;
__thread_local int last_iter,last_starti,last_startj;

#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start,dma_end,dma_start2,dma_end2;
__thread_local volatile unsigned long comp_start,comp_end;
__thread_local int core_id=0;
__thread_local int myreuse=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
extern unsigned long slave_reuse_count[64];
extern unsigned long slave_up[64];
extern unsigned long slave_up1[64]; //only halo
extern unsigned long slave_down[64];


unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

extern double A[2][SIZEX][SIZEY];

void stencil_core(targs* args)
{
   int bsize,stride,tsize; 
   int tmp[8],*buf;
#ifdef _LOOPEXP
   double tmpij,tmpij1,tmpij2,tmpij3;
#endif

   int tileno[2];
   int varno[2],reuse[2];
   int lhs_no,rhs_no;
   int dma_get_count=0;
   int dma_put_count=0;

   s_iter = args->iter;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   //buf=&(tmp[0]);
   dma_start=dma_end=dma_start2=dma_end2=0;
   //simd_load(*((intv8*)buf),args);
   //s_iter=tmp[4],s_starti=tmp[0],s_endi=tmp[1],s_startj=tmp[2],s_endj=tmp[3];
   dest = (s_iter) % 2 ; 
   src = 1 - dest;
                   
   start=rtc_();
   core_id=athread_get_id(-1);
#ifdef _LDMM
   tileno[0]=s_starti/BLKX;
   tileno[1]=s_startj/BLKY;
   slave_ldm_postreq_tile(&A[dest][0][0], 2,tileno, OUT);
   slave_ldm_postreq_tile(&A[src][0][0], 2, tileno, IN);
   slave_ldm_getreq_tile(varno,reuse);
   lhs_no = varno[0];
   rhs_no = varno[1];
  
#ifdef _PROFILING
   slave_up[core_id]+=2*(BLKX+BLKY);
   slave_down[core_id]+=BLKX*BLKY;
   slave_up1[core_id]+=2*BLKX;
   dma_start=rtc_();
#endif

   get_reply = 0;
   if(rhs_no <0)
      printf("Array assignment failed\n");
   else if(reuse[1]==0)
   {
     //1.0 update inner-core data
     stride = (SIZEY-BLKY)*8;
     tsize = BLKX*BLKY*8;
     bsize = BLKY*8;
     athread_get(PE_MODE
          ,&A[src][s_starti][s_startj], &slave[rhs_no][0][0]
          , tsize, (void*)&get_reply
          , 0, stride, bsize);
     dma_get_count++;
   }
#ifdef _PROFILING
   else
     myreuse++;
#endif
   //1.1 update up and down shade data    
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_us[0],BLKY*8,(void*)&get_reply,0,0,0); 
   athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_ds[0],BLKY*8,(void*)&get_reply,0,0,0);
   //1.2 update left and right shade data
   stride = (SIZEY-1)*8;
   tsize = BLKX*8;
   bsize = 8;
   athread_get(PE_MODE,&A[src][s_starti][s_startj-1], &A_ls[0], tsize,(void*)&get_reply, 0, stride, bsize);
   athread_get(PE_MODE,&A[src][s_starti][s_endj], &A_rs[0], tsize,(void*)&get_reply, 0, stride, bsize);  
   dma_get_count+=4;
   while(get_reply != dma_get_count); 
   
#ifdef _PROFILING
      dma_end=rtc_();
      slave_dma_count[core_id]+=((dma_end-dma_start));
      comp_start=rtc_();
#endif
//#pragma ivdep
   for(i = 1; i < BLKX-1; i++)
#ifdef _LOOPEXP
     for(j = 1; j < BLKY-1; j+=4)
      {
        tmpij=slave[rhs_no][i][j];
        tmpij1=slave[rhs_no][i][j+1];
        tmpij2=slave[rhs_no][i][j+2];
        tmpij3=slave[rhs_no][i][j+3];
        slave[lhs_no][i][j] = alpha_d * (tmpij) + \
                    beta_d * (slave[rhs_no][i-1][j] + slave[rhs_no][i+1][j] +\
                    slave[rhs_no][i][j-1] + tmpij1);
        slave[lhs_no][i][j+1] = alpha_d * (tmpij1) + \
                    beta_d * (slave[rhs_no][i-1][j+1] + slave[rhs_no][i+1][j+1] +\
                    tmpij + tmpij2);
        slave[lhs_no][i][j+2] = alpha_d * (tmpij2) + \
                    beta_d * (slave[rhs_no][i-1][j+2] + slave[rhs_no][i+1][j+2] +\
                    tmpij1+ tmpij3);
        slave[lhs_no][i][j+3] = alpha_d * (tmpij3) + \
                    beta_d * (slave[rhs_no][i-1][j+3] + slave[rhs_no][i+1][j+3] +\
                    tmpij2 + slave[rhs_no][i][j+3+1]);
      }
#else
     for(j = 1; j < BLKY-1; j++)
       slave[lhs_no][i][j] = alpha_d * (slave[rhs_no][i][j]) + \
                     beta_d * (slave[rhs_no][i-1][j] + slave[rhs_no][i+1][j] +\
                     slave[rhs_no][i][j-1] + slave[rhs_no][i][j+1]);
#endif
   slave[lhs_no][0][0] = alpha_d * (slave[rhs_no][0][0]) + \
                  beta_d * (A_us[0] + slave[rhs_no][1][0] +\
                  A_ls[0] + slave[rhs_no][0][1]);
   slave[lhs_no][BLKX-1][0] =   alpha_d * (slave[rhs_no][BLKX-1][0]) +\
                        beta_d * (slave[rhs_no][BLKX-2][0] + A_ds[0] +\
                        A_ls[BLKX-1] + slave[rhs_no][BLKX-1][1]);
   slave[lhs_no][0][BLKY-1] = alpha_d * (slave[rhs_no][0][BLKY-1]) + \
                      beta_d * (A_us[BLKY-1] +\
                      slave[rhs_no][1][BLKY-1] +\
                      slave[rhs_no][0][BLKX-2] + A_rs[0]);
   slave[lhs_no][BLKX-1][BLKY-1] = alpha_d * \
                           (slave[rhs_no][BLKX-1][BLKY-1]) + \
                           beta_d * (slave[rhs_no][BLKX-2][BLKY-1] + \
                           A_ds[BLKY-1] + \
                           slave[rhs_no][BLKX-1][BLKY-2] + \
                           A_rs[BLKX-1]);
   for(j = 1;j < BLKY-1; ++j)
   {
     slave[lhs_no][0][j] = alpha_d * (slave[rhs_no][0][j]) + \
                   beta_d * (A_us[j] + slave[rhs_no][1][j] +\
                   slave[rhs_no][0][j-1] + slave[rhs_no][0][j+1]);
     slave[lhs_no][BLKX-1][j] = alpha_d * (slave[rhs_no][BLKX-1][j]) + \
                   beta_d * (slave[rhs_no][BLKX-2][j] + A_ds[j] +\
                   slave[rhs_no][BLKX-1][j-1] + slave[rhs_no][BLKX-1][j+1]);
   }
   for(i = 1;i < BLKX-1; ++i)
   {
     slave[lhs_no][i][0] = alpha_d * (slave[rhs_no][i][0]) + \
                   beta_d * (slave[rhs_no][i-1][0] + slave[rhs_no][i+1][0] +\
                   A_ls[i] + slave[rhs_no][i][1]);
     slave[lhs_no][i][BLKY-1] = alpha_d * (slave[rhs_no][i][BLKY-1]) + \
                   beta_d * (slave[rhs_no][i-1][BLKY-1] + slave[rhs_no][i+1][BLKY-1] +\
                   slave[rhs_no][i][BLKY-2] + A_rs[i]);
   }
#ifdef _PROFILING
      comp_end=rtc_();
      slave_comp_count[core_id]+=(comp_end-comp_start);
      dma_start=rtc_();
#endif

   //write back
   put_reply=0;
   bsize=(BLKY)*8;
   stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
   tsize= bsize*(BLKX);
   athread_put(PE_MODE,&slave[lhs_no][0][0]
          , &A[dest][s_starti][s_startj]
          ,tsize,(void*)&put_reply,stride,bsize);
   while(put_reply!=1);
#ifdef _PROFILING
    dma_end=rtc_();
    slave_dma_count[core_id]+=((dma_end-dma_start));
    slave_reuse_count[core_id]=myreuse;
#endif

#else
   lhs_no=0;
   rhs_no=1;
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj)
   {
     //hit
#ifdef _PROFILING
   myreuse++;
   slave_reuse_count[core_id]=myreuse;
#endif
#ifdef _PROFILING
   slave_up[core_id]+=2*(BLKX+BLKY);
   slave_down[core_id]+=BLKX*BLKY;
   slave_up1[core_id]+=2*BLKX;
   dma_start=rtc_();
#endif
#ifdef _DMA_GET
     //1.1 update up and down shade data
     get_reply = 0;
     dma_set_size(&dma_d,BLKY*8);
     dma_set_reply(&dma_d,&get_reply);
     dma_set_op(&dma_d,DMA_GET);
     dma_set_mode(&dma_d,PE_MODE);
     dma_set_mask(&dma_d,0);
     dma_set_bsize(&dma_d,0); //bsize
     dma_set_stepsize(&dma_d,0);//stride
     dma(dma_d,&A[src][s_starti-1][s_startj],&A_us[0]);
     dma(dma_d,&A[src][s_endi][s_startj],&A_ds[0]);
     //1.2 update left,right shade, strided 
     dma_set_size(&dma_d,BLKX*8); 
     dma_set_bsize(&dma_d,0x08);
     dma_set_stepsize(&dma_d,0x3848);
     dma(dma_d,&A[src][s_starti][s_startj-1],&A_ls[0]);
     dma(dma_d,&A[src][s_starti][s_endj],&A_rs[0]);
     dma_wait(&get_reply,4);
#else
     //1.1 update up and down shade data
     get_reply = 0;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_us[0],BLKY*8,(void*)&get_reply,0,0,0);
     athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_ds[0],BLKY*8,(void*)&get_reply,0,0,0);
     //1.2 update left and right shade data,strided 
     stride = (SIZEY-1)*8;
     tsize = BLKX*8;
     bsize = 8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj-1]\
                   , &A_ls[0]\
                   , tsize,(void*)&get_reply\
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_starti][s_endj]
                   , &A_rs[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize); 
     while(get_reply != 4);
     
#endif
#ifdef _ON_CHIP_TRANSFER
//TODO:how to enable this??
     for(i=s_starti;i<s_endi;++i)
     {
        A_ls[i-s_starti]=A[src][i][s_startj-1];
        A_rs[i-s_starti]=A[src][i][s_endj];
     }  
#endif
#ifdef _PROFILING
   dma_end=rtc_();
   slave_dma_count[core_id]+=((dma_end-dma_start)); 
   comp_start=rtc_();
#endif
     //compute core data
     for(i = 1; i < BLKX-1; i++)
       for(j = 1; j < BLKY-1; j++)
          slave[rhs_no][i][j] = alpha_d * (slave[lhs_no][i][j]) + \
                     beta_d * (slave[lhs_no][i-1][j] + slave[lhs_no][i+1][j] +\
                     slave[lhs_no][i][j-1] + slave[lhs_no][i][j+1]);
     slave[rhs_no][0][0] = alpha_d * (slave[lhs_no][0][0]) + \
                   beta_d * (A_us[0] + slave[lhs_no][1][0] +\
                   A_ls[0] + slave[lhs_no][0][1]);
     slave[rhs_no][BLKX-1][0] =   alpha_d * (slave[lhs_no][BLKX-1][0]) +\
                     beta_d * (slave[lhs_no][BLKX-2][0] + A_ds[0] +\
                     A_ls[BLKX-1] + slave[lhs_no][BLKX-1][1]);
     slave[rhs_no][0][BLKY-1] = alpha_d * (slave[lhs_no][0][BLKY-1]) + \
                     beta_d * (A_us[BLKY-1] +\
                     slave[lhs_no][1][BLKY-1] +\
                     slave[lhs_no][0][BLKX-2] + A_rs[0]);
     slave[rhs_no][BLKX-1][BLKY-1] = alpha_d * \
                     (slave[lhs_no][BLKX-1][BLKY-1]) + \
                     beta_d * (slave[lhs_no][BLKX-2][BLKY-1] + \
                     A_ds[BLKY-1] + \
                     slave[lhs_no][BLKX-1][BLKY-2] + \
                     A_rs[BLKX-1]);
     for(j = 1;j < BLKY-1; ++j)
     {
       slave[rhs_no][0][j] = alpha_d * (slave[lhs_no][0][j]) + \
                     beta_d * (A_us[j] + slave[lhs_no][1][j] +\
                     slave[lhs_no][0][j-1] + slave[lhs_no][0][j+1]);
       slave[rhs_no][BLKX-1][j] = alpha_d * (slave[lhs_no][BLKX-1][j]) + \
                     beta_d * (slave[lhs_no][BLKX-2][j] + A_ds[j] +\
                     slave[lhs_no][BLKX-1][j-1] + slave[lhs_no][BLKX-1][j+1]);
     }
     for(i = 1;i < BLKX-1; ++i)
     {
        slave[rhs_no][i][0] = alpha_d * (slave[lhs_no][i][0]) + \
                     beta_d * (slave[lhs_no][i-1][0] + slave[lhs_no][i+1][0] +\
                     A_ls[i] + slave[lhs_no][i][1]);
        slave[rhs_no][i][BLKY-1] = alpha_d * (slave[lhs_no][i][BLKY-1]) + \
                     beta_d * (slave[lhs_no][i-1][BLKY-1] + slave[lhs_no][i+1][BLKY-1] +\
                     slave[lhs_no][i][BLKY-2] + A_rs[i]);

     }
#ifdef _PROFILING
   comp_end=rtc_();
   slave_comp_count[core_id]+=(comp_end-comp_start);
   dma_start=rtc_();
#endif
     //write back
     put_reply=0;
     bsize=(BLKY)*8;
     stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(BLKX);
     athread_put(PE_MODE,&slave[rhs_no][0][0]
                , &A[dest][s_starti][s_startj]
                ,tsize,(void*)&put_reply,stride,bsize);
     while(put_reply!=1);
#ifdef _PROFILING
   dma_end=rtc_();
   slave_dma_count[core_id]+=((dma_end-dma_start));
#endif
   }//end of hit
   else{
     //data miss 
#ifdef _PROFILING
   slave_up[core_id]+=BLKX*BLKY+2*(BLKX+BLKY);
   slave_down[core_id]+=BLKX*BLKY;

   dma_start=rtc_();
#endif
     get_reply = 0;
     //1.0 update inner-core data
     stride = (SIZEY-BLKY)*8;
     tsize = BLKX*BLKY*8;
     bsize = BLKY*8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj]
                   , &slave[rhs_no][0][0]
                   , tsize, (void*)&get_reply
                   , 0, stride, bsize);
     //1.1 update up and down shade data    
     
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_us[0],BLKY*8,(void*)&get_reply,0,0,0); 
     athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_ds[0],BLKY*8,(void*)&get_reply,0,0,0);
     //1.2 update left and right shade data
     stride = (SIZEY-1)*8;
     tsize = BLKX*8;
     bsize = 8;
     athread_get(PE_MODE,&A[src][s_starti][s_startj-1]
                   , &A_ls[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_starti][s_endj]
                   , &A_rs[0]
                   , tsize,(void*)&get_reply
                   , 0, stride, bsize);  
     
     while(get_reply != 5);
#ifdef _ON_CHIP_TRANSFER
//TODO:how to enable this??
     for(i=s_starti;i<s_endi;++i)
     {
        A_ls[i-s_starti]=A[src][i][s_startj-1];
        A_rs[i-s_starti]=A[src][i][s_endj];
     }
#endif

#ifdef _PROFILING
   dma_end=rtc_();
   slave_dma_count[core_id]+=((dma_end-dma_start));
   comp_start=rtc_();
#endif
     //2.compute core data
     for(i = 1; i < BLKX-1; i++)
       for(j = 1; j < BLKY-1; j++)
        {
          slave[lhs_no][i][j] = alpha_d * (slave[rhs_no][i][j]) + \
                     beta_d * (slave[rhs_no][i-1][j] + slave[rhs_no][i+1][j] +\
                     slave[rhs_no][i][j-1] + slave[rhs_no][i][j+1]);
        }
     //3.1compute up_left_point
     slave[lhs_no][0][0] = alpha_d * (slave[rhs_no][0][0]) + \
                   beta_d * (A_us[0] + slave[rhs_no][1][0] +\
                   A_ls[0] + slave[rhs_no][0][1]); 
     //3.2compute up_right_point
     slave[lhs_no][BLKX-1][0] =   alpha_d * (slave[rhs_no][BLKX-1][0]) +\
                     beta_d * (slave[rhs_no][BLKX-2][0] + A_ds[0] +\
                     A_ls[BLKX-1] + slave[rhs_no][BLKX-1][1]);
     //3.3compute up_right_point
     slave[lhs_no][0][BLKY-1] = alpha_d * (slave[rhs_no][0][BLKY-1]) + \
                     beta_d * (A_us[BLKY-1] +\
                     slave[rhs_no][1][BLKY-1] +\
                     slave[rhs_no][0][BLKX-2] + A_rs[0]);
     //3.4compute down_right_point
     slave[lhs_no][BLKX-1][BLKY-1] = alpha_d * \
                     (slave[rhs_no][BLKX-1][BLKY-1]) + \
                     beta_d * (slave[rhs_no][BLKX-2][BLKY-1] + \
                     A_ds[BLKY-1] + \
                     slave[rhs_no][BLKX-1][BLKY-2] + \
                     A_rs[BLKX-1]);
     //4.1 compute up and down line of j dimensional  change 
     for(j = 1;j < BLKY-1; ++j)
     {
       slave[lhs_no][0][j] = alpha_d * (slave[rhs_no][0][j]) + \
                     beta_d * (A_us[j] + slave[rhs_no][1][j] +\
                     slave[rhs_no][0][j-1] + slave[rhs_no][0][j+1]);
       slave[lhs_no][BLKX-1][j] = alpha_d * (slave[rhs_no][BLKX-1][j]) + \
                     beta_d * (slave[rhs_no][BLKX-2][j] + A_ds[j] +\
                     slave[rhs_no][BLKX-1][j-1] + slave[rhs_no][BLKX-1][j+1]);
     }
     //4.2 compute left and right line of i dimensional change
     for(i = 1;i < BLKX-1; ++i)
     {
        slave[lhs_no][i][0] = alpha_d * (slave[rhs_no][i][0]) + \
                     beta_d * (slave[rhs_no][i-1][0] + slave[rhs_no][i+1][0] +\
                     A_ls[i] + slave[rhs_no][i][1]);
        slave[lhs_no][i][BLKY-1] = alpha_d * (slave[rhs_no][i][BLKY-1]) + \
                     beta_d * (slave[rhs_no][i-1][BLKY-1] + slave[rhs_no][i+1][BLKY-1] +\
                     slave[rhs_no][i][BLKY-2] + A_rs[i]);

     }
#ifdef _PROFILING
   comp_end=rtc_();
   slave_comp_count[core_id]+=(comp_end-comp_start);
   dma_start=rtc_();
#endif
     //5:write back
     put_reply=0;
     bsize=(BLKY)*8;
     stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead
     tsize= bsize*(BLKX);
     athread_put(PE_MODE,&slave[lhs_no][0][0]
                , &A[dest][s_starti][s_startj]
                ,tsize,(void*)&put_reply,stride,bsize);
     while(put_reply!=1);
#ifdef _PROFILING
   dma_end=rtc_();
   slave_dma_count[core_id]+=((dma_end-dma_start));
#endif
    last_iter = s_iter;
    last_starti = s_starti;
    last_startj = s_startj;
   }
#endif
}
